﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Xml;

namespace GempGame
{
    public enum NodeType
    {
        Market,
        SegmentationLevel,
        Strategy,
        Production,
        Company
    };

    public class Level
    {
        public string name { get; set; }
        public string description { get; set; }
        public int goal { get; set; }
        public int limit { get; set; }
        public string variable { get; set; }
    }

    public class Option : IComparable
    {
        //Nome
        public string Name { get; set; }

        //Descricao
        public string Description { get; set; }

        //Tipo do nó. Pode ser "mercado", "nivelSegmentacao", "massas", etc
        public NodeType Type { get; set; }

        //Nivel do nó em relação à raiz da árvore
        //Parametro carregado no ficheiro de configuracao
        public Int32 Level { get; set; }

        //Conjunto de falas associadas ao nó
        public ArrayList Talks { get; set; }

        //Percentagem de influencia deste nó
        public double Influence { get; set; }

        //Percentagem associada ao no que deriva das sub opcoes do mesmo, o no tem que ser finalNode = true
        public double choicesPercentage { get; set; }

        //Opcoes possiveis a partir deste nó
        public List<Option> Options { get; set; }
        
        // Opcao do jogador
        // Corresponde ao indice da opção escolhida na lista Options
        public Int32 Choice { get; set; }

        //Se true, significa que jogador apenas tem de escolher uma das opções.
        //Se false significa que o jogador tem de fazer escolhas em todos os filhos. 
        //Se false, parametro Choice tem de ser nulo.
        //Ex. nó mercado não é finalNode porque jogador tem de escolher estratégia para todos os nós filhos: segmentação, organização, etc.
        public bool finalNode { get; set; }


        //PARA O PARSER
        public string attName { get; set; }
        public ArrayList OptionsName { get; set; }
        public string market { get; set; }
        public string parentName { get; set; }

        public static string productName { get; set; }
        public static string productDescription { get; set; }
        public List<Level> levels { get; set; }


        
        public Option() 
        {
            this.Choice = 0;
            this.finalNode = true;
            this.Options = new List<Option>();
            this.OptionsName = new ArrayList();
            this.Talks = new ArrayList();
            this.attName = "";
            this.levels = new List<Level>();
        }
        
        public Option(string name, string description)
        {
            this.Name = name;
            this.Description = description;
            this.finalNode = true;
            this.Choice = 0;
            this.Options = new List<Option>();
            this.OptionsName = new ArrayList();
            this.Talks = new ArrayList();
            this.attName = "";
            this.levels = new List<Level>();
        }

        public Option(string name, string description, double influence)
        {
            this.Name = name;
            this.Description = description;
            this.Influence = influence;
            this.finalNode = true;
            this.Choice = 0;
            this.Options = new List<Option>();
            this.OptionsName = new ArrayList();
            this.Talks = new ArrayList();
            this.attName = "";
            this.levels = new List<Level>();
        }

        public Option chooseOption()
        {
            if (this.Options.Count == 0)
            {
                return null;
            }
            Console.WriteLine("Opcao de " + this.Name);
            for (int i = 0; i < this.Options.Count; i++)
            {
                Console.WriteLine("   " + i + ": " + this.Options[i].Name);
            }

            string opcString = "";
            int opc = -1;
            while (opc < 0 || opc >= this.Options.Count)
            {
                Console.Write("Escolha: ");
                opcString = Console.ReadLine();
                if (int.TryParse(opcString, out opc) == true)
                    opc = int.Parse(opcString);
                else
                    opc = -1;
            }
            this.Choice = opc;

            return this.Options[opc];
        }



        // Metodo recursivo. Chamar com aux=0
        public List<string> generateTalks(int numTalks)
        {
            //Retorna de nó nao tiver filhos
            if (Options.Count == 0)
            {
                return randomizeArray(this.Talks, numTalks).Cast<string>().ToList<string>();
            }

            //Se houver filhos 
            else
            {
                //Cria array com talks deste nó
                ArrayList higherLevelTalks = 
                    new ArrayList( randomizeArray(this.Talks, numTalks) );

                //Vai buscar falas do nó escolhido
                if (finalNode)
                {
                    higherLevelTalks.AddRange( ((Option) Options[Choice]).generateTalks(numTalks) );
                }

                //Vai buscar falas dos nós filhos. Como não é finalNode precisa das falas de todos os filhos
                else
                {
                    foreach (Option opt in this.Options)
                    {
                        higherLevelTalks.AddRange(opt.generateTalks(numTalks));
                    }
                }

                return higherLevelTalks.Cast<string>().ToList<string>();
            }
        }

        private ArrayList randomizeArray(ArrayList array, int size)
        {
            ArrayList generatedArray = new ArrayList();
            int generatedNumber = -1;
            Random rand = new Random();
            int max = array.Count;

            for (int i = 0; i < size; i++)
            {
                generatedNumber = rand.Next(max);
                generatedArray.Add(array[generatedNumber]);
            }

            return generatedArray;
        }



        public double generatePercentage()
        {
            //Console.WriteLine("Calcula Percentagem no: " + this.Name);
            //Retorna de nó nao tiver filhos
            if (Options.Count == 0)
            {
                //Console.WriteLine(this.Name + " Retorna Influencia: " + this.Influence);
                return this.Influence;
            }
            //Se houver filhos 
            else
            {
                double currentPercentage = 0;

                //Vai buscar percentagens do nó escolhido
                if (finalNode)
                {
                    currentPercentage = this.Influence;
                    //Console.WriteLine(this.Name + " Final Node: " + currentPercentage + " Choice: " + this.Choice);
                    double temp = ((Option)Options[Choice]).generatePercentage();
                    this.choicesPercentage = temp;
                    currentPercentage *= temp;
                }

                //Vai buscar percentagens dos nós filhos. Como não é finalNode precisa das influencias de todos os filhos
                else
                {
                    foreach (Option opt in this.Options)
                    {
                        currentPercentage += this.Influence*opt.generatePercentage();
                    }
                }

                //Console.WriteLine(this.Name + " Retorna currentPercentage: " + currentPercentage);
                return currentPercentage;
            }
        }

        public int CompareTo(object obj)
        {
            return this.attName.CompareTo(((Option)obj).attName);
        }

        public static Option getOptionByName(Option root, string name)
        {
            if (root.attName.CompareTo(name) == 0)
            {
                return root;
            }
            if (root.Options.Count == 0)
            {
                return null;
            }
            else
            {
                foreach (Option opt in root.Options)
                {
                    if (Option.getOptionByName(opt, name) != null)
                        return Option.getOptionByName(opt, name);
                }
            }
            return null;
        }

        public static List<Option> loadGame(string nomeFicheiro)
        {
            //XmlReaderSettings settings = new XmlReaderSettings();
            //XmlReader reader = XmlReader.Create(nomeFicheiro, Encoding.GetEncoding("ISO-8859-1"));
            XmlDocument loadFich = new XmlDocument();

            loadFich.Load(nomeFicheiro);

            XmlNodeList listProduct = loadFich.GetElementsByTagName("Product");
            if (listProduct.Count == 1)
                Option.productName = listProduct[0].Attributes["name"].InnerXml.ToString();
            else
                Option.productName = "Não há nome disponível!";

            XmlNodeList listDesc = loadFich.GetElementsByTagName("DescricaoProd");
            if (listDesc.Count == 1)
                Option.productDescription = listDesc[0].InnerXml.ToString();
            else
                Option.productDescription = "Não há descrição disponível!";


            XmlNodeList listMarkets = loadFich.GetElementsByTagName("Market");
            
            List<Option> marketsOptions = new List<Option>();

            //Percorre Markets
            foreach (XmlNode market in listMarkets)
            {

                //Nome do mercado
                string marketName = market["Name"].InnerXml;
                //Console.WriteLine("" + marketName);
                //Influencia
                string influence = market["Influence"].InnerXml;
                //Console.WriteLine("" + influence);
                //Descricao
                string description = market["Description"].InnerXml;
                //Console.WriteLine("" + description);

                string finalNode = market.Attributes["finalNode"].InnerXml;
                //Console.WriteLine("" + finalNode);

                string attName = market.Attributes["name"].InnerXml;

                Option MarketOpt = new Option(marketName, description, double.Parse(influence));
                MarketOpt.finalNode = bool.Parse(finalNode);
                MarketOpt.attName = attName;

                ArrayList talksArray = new ArrayList();
                ArrayList subOptions = new ArrayList();

                //Percorre filhos do mercado
                foreach (XmlNode marketChild in market.ChildNodes)
                {
                    //Caso seja talksOption
                    if (marketChild.Name.CompareTo("TalksOption") == 0)
                    {
                        XmlNodeList talksList = marketChild.ChildNodes;

                        //Percorre as talks
                        foreach (XmlNode talk in talksList)
                        {
                            string talkString = talk.InnerXml;
                            talksArray.Add(talkString);
                            //Console.WriteLine(talkString);
                        }
                    }
                    //Caso seja SubOptions
                    else if (marketChild.Name.CompareTo("SubOptions") == 0)
                    {
                        XmlNodeList optionsList = marketChild.ChildNodes;

                        //Percorre option
                        foreach (XmlNode option in optionsList)
                        {
                            string optionString = option.InnerXml;
                            //Console.WriteLine(optionString);
                            subOptions.Add(optionString);
                        }
                    }
                    //Niveis
                    else if (marketChild.Name.CompareTo("Levels") == 0 && marketChild.Attributes["market"].InnerXml.CompareTo(MarketOpt.attName) == 0)
                    //else if (marketChild.Name.CompareTo("Levels") == 0)
                    {
                        XmlNodeList levelsList = marketChild.ChildNodes;

                        foreach (XmlNode levelNode in levelsList)
                        {
                            Level level = new Level();
                            level.name = levelNode["Name"].InnerXml;
                            level.variable = levelNode["Variable"].InnerXml;
                            level.goal = int.Parse(levelNode["Goal"].InnerXml);
                            level.limit = int.Parse(levelNode["Limit"].InnerXml);
                            level.description = levelNode["goalDescription"].InnerXml;
                            //System.Console.WriteLine("Novo Nivel: " + level.name + " mercado: " + MarketOpt.Name + "  -  " + marketChild.Attributes["market"].InnerXml);
                            MarketOpt.levels.Add(level);
                        }
                    }
                }

                MarketOpt.Talks = talksArray;
                MarketOpt.OptionsName = subOptions;
                marketsOptions.Add(MarketOpt);
            }

            //Console.WriteLine("\n\nOPTIONS:");
            XmlNodeList listOptions = loadFich.GetElementsByTagName("Option");

            List<Option> allOptions = new List<Option>();

            //Percorre options
            foreach (XmlNode option in listOptions)
            {
                string optionName = option["Name"].InnerText;
                //Console.WriteLine(optionName);
                string attName = option.Attributes["name"].InnerXml;
                //Console.WriteLine("     " + attName);
                string parentName = option.Attributes["parent"].InnerXml;
                //Console.WriteLine("     " + parentName);
                string marketName = option.Attributes["market"].InnerXml;
                //Console.WriteLine("     " + marketName);
                string finalNode = option.Attributes["finalNode"].InnerXml;
                //Console.WriteLine("     " + finalNode);
                string optionInfluence = option["Influence"].InnerXml;
                //Console.WriteLine("     " + optionInfluence);
                string optionDescription = option["Description"].InnerXml;
                //Console.WriteLine("     " + optionDescription);

                Option opt = new Option(optionName, optionDescription, double.Parse(optionInfluence));
                opt.attName = attName;
                opt.market = marketName;
                opt.finalNode = bool.Parse(finalNode);
                opt.parentName = parentName;

                ArrayList subOptions = new ArrayList();
                ArrayList talksArray = new ArrayList();

                XmlNodeList optionList = option.ChildNodes;
                foreach (XmlNode optionChild in optionList)
                {
                    if (optionChild.Name.CompareTo("SubOptions") == 0)
                    {
                        foreach (XmlNode subOption in optionChild.ChildNodes)
                        {
                            string subOptionName = subOption.InnerXml;
                            subOptions.Add(subOptionName);
                            //Console.WriteLine("     " + subOptionName);
                        }
                    }
                    else if (optionChild.Name.CompareTo("TalksOption") == 0)
                    {
                        foreach (XmlNode talk in optionChild.ChildNodes)
                        {
                            string talkString = talk.InnerXml;
                            talksArray.Add(talkString);
                            //Console.WriteLine("     " + talkString);
                        }
                    }
                }

                opt.OptionsName = subOptions;
                opt.Talks = talksArray;
                allOptions.Add(opt);
            }
            //Console.WriteLine("\n\nOPTIONS: " + allOptions.Count);

            //Percorre todas as options e cria a arvore
            foreach (Option optFather in allOptions)
            {
                //Percorre nomes das subOptions
                foreach (string optChildName in optFather.OptionsName)
                {
                    //Percorre todas as options
                    for (int i = 0; i < allOptions.Count; i++)
                    {
                        if (((Option)allOptions[i]).attName.CompareTo(optChildName) == 0 && ((Option)allOptions[i]).market.CompareTo(optFather.market) == 0 && ((Option)allOptions[i]).parentName.CompareTo(optFather.attName) == 0)
                        {
                            optFather.Options.Add((Option)allOptions[i]);
                        }
                    }
                }
            }

            //Percorre mercados
            foreach (Option mrk in marketsOptions)
            {
                //Percorre nomes das subOptions
                foreach (string optName in mrk.OptionsName)
                {
                    //Percorre percorre as options
                    for (int i = 0; i < allOptions.Count; i++)
                    {
                        if (((Option)allOptions[i]).attName.CompareTo(optName) == 0 && ((Option)allOptions[i]).market.CompareTo(mrk.Name) == 0)
                        {
                            mrk.Options.Add((Option)allOptions[i]);
                        }
                    }
                }
            }

            return marketsOptions;
        }
    }
}
